package com.beeasy.tools.bean;

import cn.hutool.core.collection.ConcurrentHashSet;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.experimental.Accessors;
import ma.glasnost.orika.MapperFactory;
import ma.glasnost.orika.impl.DefaultMapperFactory;

import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentSkipListSet;
import java.util.concurrent.locks.ReentrantLock;

public class MapperFactoryHolder {

    private InitHandler initHandler;
    private Map<MapperKey, MapperFactoryHolderItem> index = new ConcurrentHashMap<>();
    private Set<MapperFactoryHolderItem> set = new ConcurrentSkipListSet<>();
    private ReentrantLock lock = new ReentrantLock();
    /**
     * 是否忽略null
     */
    private boolean ignoreNull;

    public MapperFactoryHolder setIgnoreNull(boolean ignoreNull) {
        this.ignoreNull = ignoreNull;
        return this;
    }

    public MapperFactoryHolder(InitHandler initHandler) {
        this.initHandler = initHandler;
    }

    public MapperFactory getMapperFactory(Class src, Class dest){
        MapperKey key = new MapperKey(src, dest);
        MapperFactoryHolderItem result = index.get(key);
        if (result != null) {
            return result.getMapperFactory();
        }
        lock.lock();
        try {
            for (MapperFactoryHolderItem item : set) {
                //同时不包括的时候可以使用
                Set<Class> cache = item.getCache();
                if (!cache.contains(src) && !cache.contains(dest)) {
                    cache.add(src);
                    cache.add(dest);
                    //添加索引
                    index.put(key, item);
                    if (initHandler != null) {
                        initHandler.init(src, dest, item.getMapperFactory());
                    }
                    return item.getMapperFactory();
                }
            }
            //如果没有找到，创建新的
            MapperFactoryHolderItem item = new MapperFactoryHolderItem(ignoreNull);
            set.add(item);
            item.getCache().add(src);
            item.getCache().add(dest);
            //添加索引
            index.put(key, item);
            if (initHandler != null) {
                initHandler.init(src, dest, item.getMapperFactory());
            }
            return item.getMapperFactory();
        } finally {
            lock.unlock();
        }
    }

    @Data
    @Accessors(chain = true)
    public static class MapperFactoryHolderItem implements Comparable{
        private Set<Class> cache = new ConcurrentHashSet<>();
        private MapperFactory mapperFactory; //= new DefaultMapperFactory.Builder().build();

        public MapperFactoryHolderItem(boolean ignoreNull){
            DefaultMapperFactory.Builder builder = new DefaultMapperFactory.Builder();
            if(ignoreNull){
                builder.mapNulls(false);
            }
            mapperFactory = builder.build();
        }

        @Override
        public int compareTo(Object o) {
            return Integer.compare(hashCode(), o.hashCode());
        }
    }


    @Data
    @AllArgsConstructor
    public static class MapperKey{
        private Class src;
        private Class dest;
    }


    public interface InitHandler {
        public void init(Class src, Class dest, MapperFactory mapperFactory);
    }
}
